using System.IO;
using System.Linq;

namespace Legend.IO
{
    /// <summary>
    /// The default implementation of the IFileSystem-interface. Uses the
    /// System.IO-classes.
    /// </summary>
    public class DefaultFileSystem
        : IFileSystem
    {
        /// <summary>
        /// Gets a file object.
        /// </summary>
        /// <param name="filePath">The path of the file to get.</param>
        /// <returns>A file object.</returns>
        public IFile GetFile(string filePath)
        {
            return new File(new FileInfo(filePath));
        }

        /// <summary>
        /// Gets a directory object.
        /// </summary>
        /// <param name="directoryPath">The path of the directory to get.</param>
        /// <returns>A directory object.</returns>
        public IDirectory GetDirectory(string directoryPath)
        {
            return new Directory(new DirectoryInfo(directoryPath));
        }

        private abstract class DefaultFileSystemEntry
            : IFileSystemEntry
        {
            private FileSystemInfo fileSystemInfo;
            protected DefaultFileSystemEntry(FileSystemInfo fileSystemInfo)
            {
                this.fileSystemInfo = fileSystemInfo;
            }

            public string Name
            {
                get { return this.fileSystemInfo.Name; }
            }

            public string FullName
            {
                get { return this.fileSystemInfo.FullName; }
            }

            public bool Exists
            {
                get { return this.fileSystemInfo.Exists; }
            }

            public static IFileSystemEntry Create(FileSystemInfo fileSystemInfo)
            {
                var file = fileSystemInfo as FileInfo;
                if (file != null)
                {
                    return new File(file);
                }
                else
                {
                    return new Directory((DirectoryInfo)fileSystemInfo);
                }
            }


            public void Delete()
            {
                this.fileSystemInfo.Delete();
            }


            public System.DateTime CreationTime
            {
                get { return this.fileSystemInfo.CreationTime; }
            }

            public System.DateTime TimeOfModification
            {
                get { return this.fileSystemInfo.LastWriteTime; }
            }
        }

        private class File
            : DefaultFileSystemEntry, IFile
        {
            private FileInfo fileInfo;
            public File(FileInfo fileInfo)
                : base(fileInfo)
            {
                this.fileInfo = fileInfo;
            }

            public Stream Open()
            {
                return this.fileInfo.Open(FileMode.OpenOrCreate);
            }
        }

        private class Directory
            : DefaultFileSystemEntry, IDirectory
        {
            DirectoryInfo directoryInfo;

            public Directory(DirectoryInfo directoryInfo)
                : base(directoryInfo)
            {
                this.directoryInfo = directoryInfo;
            }



            public System.Collections.Generic.IEnumerable<IFileSystemEntry> Entries
            {
                get
                {
                    return
                        from entry in this.directoryInfo.EnumerateFileSystemInfos()
                        select DefaultFileSystemEntry.Create(entry);
                }
            }

            public System.Collections.Generic.IEnumerable<IFile> Files
            {
                get
                {
                    return
                        (from file in this.directoryInfo.EnumerateFiles()
                        select new File(file)).Cast<IFile>();
                }
            }

            public System.Collections.Generic.IEnumerable<IDirectory> Directories
            {
                get
                {
                    return
                        (from directory in this.directoryInfo.EnumerateDirectories()
                        select new Directory(directory)).Cast<IDirectory>();
                }
            }


            public void DeleteRecursively()
            {
                this.directoryInfo.Delete(true);
            }
        }

    }
}
